// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
// 
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// Copyright (c) 2004 Scott Willeke (http://scott.willeke.com)
//
// Authors:
//	Scott Willeke (scott@willeke.com)
//
#region Using directives

using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using LessMsi.Msi;
using LessMsi.UI;

#endregion

namespace LessMsi
{
    public class Program
    {
        [DllImport("kernel32.dll", SetLastError = true)]
        static extern int FreeConsole();

        public class Arguments
        {
            public string MsiFileName { get; set; }
            public string OutDirName { get; set; }

            public ProgramAction Action { get; set; }
        }

        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static int Main(string[] argStrings)
        {
            try
            {
                Arguments args = ParseArguments(argStrings);

                switch (args.Action)
                {
                    case ProgramAction.ExtractAndQuit:
                        DoExtraction(args.MsiFileName, args.OutDirName);
                        return 0;

                    case ProgramAction.ShowUsageAndQuit:
                        return -2;

                    case ProgramAction.OpenGui:
                        return LaunchForm(args.MsiFileName);

                    default:
                        throw new ApplicationException(string.Format("Unknow action {0}", args.Action));
                }
            }
            catch (Exception eCatchAll)
            {
                ShowHelp(eCatchAll.ToString());
                return -1;
            }
        }

        /// <summary>
        /// Extracts all files contained in the specified .msi file into the specified output directory.
        /// </summary>
        /// <param name="msiFileName">The path of the specified MSI file.</param>
        /// <param name="outDirName">The directory to extract to. If empty it will use the current directory.</param>
        public static void DoExtraction(string msiFileName, string outDirName)
        {
            EnsureFileRooted(ref msiFileName);
            EnsureFileRooted(ref outDirName);

            FileInfo msiFile = new FileInfo(msiFileName);
            DirectoryInfo outDir = new DirectoryInfo(outDirName);

            Console.WriteLine("Extracting \'" + msiFile + "\' to \'" + outDir + "\'.");

            Wixtracts.ExtractFiles(msiFile, outDir);
        }

        public static Arguments ParseArguments(string[] args)
        {
            if (args.Length == 0)
                return new Arguments { Action = ProgramAction.OpenGui };

            if (args.Length == 1)
                return new Arguments { Action = ProgramAction.OpenGui, MsiFileName = args[0] };

            if (args[0].StartsWith("/") || args[0].StartsWith("-"))
                switch (args[0].Substring(1).ToLowerInvariant())
                {
                    case "x":
                        var msiFileName = args[1];

                        return new Arguments
                        {
                            Action = ProgramAction.ExtractAndQuit,
                            MsiFileName = msiFileName,
                            OutDirName = args.Length == 2 ? Path.GetDirectoryName(msiFileName) : args[2]
                        };
                }

            ShowHelp(null);
            return new Arguments { Action = ProgramAction.ShowUsageAndQuit };
        }

        private static void ShowHelp(string errorMessage)
        {
            string helpString =
                @"Usage:
lessmsi [/x <msiFileName> [<outouptDir]]

/x <msiFileName>                    Extract contents of the 
                                    specified msi file into a 
                                    new directory in the same 
                                    directory as the msi.

/x <msiFileName> <outouptDir>       Extract files in the specified 
                                    msi file into a directory at the 
                                    same place as the .msi file.
";
            if (!string.IsNullOrEmpty(errorMessage))
            {
                helpString = "\r\nError: " + errorMessage + "\r\n\r\n" + helpString;
            }
            Console.WriteLine(helpString);
        }

        private static void EnsureFileRooted(ref string sFileName)
        {
            if (!Path.IsPathRooted(sFileName))
                sFileName = Path.Combine(Directory.GetCurrentDirectory(), sFileName);
        }

        static int LaunchForm(string inputFile)
        {
            FreeConsole();
            Application.EnableVisualStyles();
            Application.DoEvents();// make sure EnableVisualStyles works.
            MainForm form = new MainForm(inputFile);
            Application.Run(form);
            return 0;
        }
    }
}